/*
 * Andy Hains
 * CS426 Project 2
 * Due: 11/22/2011
 */

/*
 * File:  main.c
 * --------------------
 * The code for the program that will use the buffer.
 */

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include "buffer.h"
#include "buffer.c"
#include "robot.h"
#include "robot.c"

#define MAX_SLEEP 5; //max time for threads to sleep, in seconds

typedef struct {
    unsigned char state;
    time_t timestamp;
} bumper_state_t;

int quitFlag = 0;
bumper_state_t bumper_state;
pthread_mutex_t bumper_mutex = PTHREAD_MUTEX_INITIALIZER;

void *watchKeyboard(void*);
void *manageSerial(void*);
void *bumperTimer(void*);
void *producer(void*);
void *consumer(void*);

int main(int argc, char *argv[]) {
    int ret = 0;

    // 1. Initialize the serial port to the robot and the command buffer
	if(robot_init(argv[1]) < 0) {
		fprintf(stderr, "could not connect to the robot\n");
		exit(1);
	}

    if (buffer_init() != 0) {
        fprintf(stderr, "Error: could not initialize buffer\n");
        return EXIT_FAILURE;
    }

    // 2. Create threads
    pthread_t tKeyboard;
    pthread_t tSerial;
    pthread_t tBumpers;
    if (pthread_create(&tKeyboard, NULL, &watchKeyboard, NULL) != 0)
        return EXIT_FAILURE;
    if (pthread_create(&tSerial, NULL, &manageSerial, NULL) !=0)
        return EXIT_FAILURE;
    if (pthread_create(&tBumpers, NULL, &bumperTimer, NULL) !=0)
        return EXIT_FAILURE;

    // 3. Wait for the manageSerial thread to return, then quit threads and exit
    if (pthread_join(tSerial, NULL) != 0)
        return EXIT_FAILURE;
//    if (pthread_join(tSerial, NULL) != 0)
//        return EXIT_FAILURE;
//    if (pthread_join(tBumpers, NULL) != 0)
//        return EXIT_FAILURE;

    printf("--Finished Keyboard Thread--\n");
    quitFlag = 1;
    ret += pthread_cancel(tKeyboard);
    ret += pthread_cancel(tBumpers);

    ret += buffer_cleanup();

    return ret;
}


void *watchKeyboard(void* param) {
/*
 * Funciton: watchKeyboard
 * --------------------
 * This is the start function for a producer thread that will monitor the 
 * keyboard for input. When a character is entered from the keyboard, it is 
 * added to the buffer.
 *
 * param: optional parameter argument, not used
 *
 * returns: none
 */

    unsigned char c;

	while(!quitFlag) {
        // get a character from the keyboard
		scanf("%c", &c);

        // add the character from the keyboard to the buffer
        if (c != 10) {
            if (insert_item((buffer_item_t)c))
                fprintf(stderr, "report error condition");
            else
                printf("watchKeyboard produced %c\n", c);
        }
	}
}

void *manageSerial(void* param) {
/*
 * Function: manageSerial
 * --------------------
 * This is the start function for the thread that will manage the serial port.
 *
 * This thread is a consumer that will simply take commands from the buffer and 
 * send them to the robot and, if necessary, listen for a response.
 *
 * param: optional parameter argument, not used
 *
 * returns: none
 */
    buffer_item_t c;
	char bumper;
	time_t start_time;
	time_t stamp;

	start_time = time(NULL);

    while (!quitFlag) {
        if (remove_item(&c))
            fprintf(stderr, "report error condition");
        else
            printf("manageSerial consumed %c\n", c);

		//printf("> ");
//		scanf("%c", &c);
		if(c == 'q') {
            robot_stop();
			robot_close();
            return;
//			exit(0);
		}
		else if(c == 'f') {
			robot_forward();
		}
		else if(c == 'b') {
			robot_backward();
		}
		else if(c == 'l') {
			robot_left();
		}
		else if(c == 'r') {
			robot_right();
		}
		else if(c == 's') {
			robot_stop();
		}
		else if(c == 't') {
			robot_bumpers(&bumper, &stamp);
            pthread_mutex_lock(&bumper_mutex);
            bumper_state.state = bumper;
            bumper_state.timestamp = stamp;
            pthread_mutex_unlock(&bumper_mutex);

			printf("bumbers: ");
			if(bumper < 0 || bumper > 3) {
				printf("Invalid bumper data\n");
			}
			printf("left=%s   ", ((bumper & 2) == 2) ? "yes" : "no");
			printf("right=%s   ", ((bumper & 1) == 1) ? "yes" : "no");
			printf("time=%d\n", (int) difftime(stamp, start_time));
		}
    }
}

void *bumperTimer(void* param) {
/*
 * Function: bumperTimer
 * --------------------
 * This is the start function for the thread that will periodically add a 
 * command to the buffer that will request the state of the bumpers.
 *
 * param: optional parameter argument, not used
 *
 * returns: none
 */
    buffer_item_t c = 't';

    while (!quitFlag) {
        if (insert_item(c))
            fprintf(stderr, "report error condition");
        else
            printf("bumperTimer produced %c\n", c);

        sleep(2);
    }
}



void *producer(void* param) {
/*
 * Function:  producer
 * --------------------
 * This is the start function for all of the producer threads
 * Runs a loop that sleeps and then adds an item to the buffer.
 * Will loop until quitFlag is set.
 * 
 * param: optional parameter argument, not used
 *
 * returns: none
 */
    buffer_item_t item;
    unsigned int s;

    while (!quitFlag) {
        // sleep for a random period of time
        s = rand() % MAX_SLEEP + 1;
        sleep(s);

        // generate a random number
        item = rand();

        if (insert_item(item))
            fprintf(stderr, "report error condition");
        else
            printf("producer produced %d\n", item);
    }
}

void *consumer(void* param) {
/*
 * Function:  consumer
 * --------------------
 * This is the start function for all of the consumer threads.
 * Runs a loop that sleeps and then consumes an item from the buffer.
 * Will loop until quitFlag is set.
 * 
 * param: optional parameter argument, not used
 *
 * returns: none
 */
    buffer_item_t item;
    unsigned int s;

    while (!quitFlag) {
        // sleep for a random period of time
        s = rand() % MAX_SLEEP + 1;
        sleep(s);

        if (remove_item(&item))
            fprintf(stderr, "report error condition");
        else
            printf("consumer consumed %d\n", item);
    }
}

